#include <freeradius-devel/ident.h>
RCSID("$Id$")

#include <freeradius-devel/libradius.h>
#include <freeradius-devel/conf.h>
#include <freeradius-devel/radpaths.h>

#include <ctype.h>

#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif

#include <assert.h>
#include <string.h>

#include <pthread.h>
#include "mytimer.h"
#include "configure.h"

#include "server.h"


typedef struct rad_recv_queue
{
    struct rad_recv_queue *next;
    void *buf;
    int num;
} rad_recv_queue;


/****** Extern  variables******/
extern int do_radius_test;
extern int do_radius_account;
extern const char *radius_shared_key;
extern const char *radius_server_ipv6;
extern const char *radius_server_ipv4;
extern const char *radius_reserve_server_ipv6;
extern const char *radius_reserve_server_ipv4;
extern int radius_default_afnum;
extern const char *local_server_ipv6;
extern const char *local_server_ipv4;

/****** Global  variables******/
char nas_ip4addr[20];
char nas_ip6addr[50];
static int retries = RAD_RESEND_TIMES;
//static float timeout = 5;
static const char *hostname = RAD_SERVER_HOST_NAME;
static const char *secret;
//static int do_output = 1;
//static int totalapp = 0;
//static int totaldeny = 0;
//static int totallost = 0;

//static int server_port = 0;
//static int packet_code = 0;
static fr_ipaddr_t server_ipaddr;
static fr_ipaddr_t server_ipaddr6;
static fr_ipaddr_t reserve_server_ipaddr;
static fr_ipaddr_t reserve_server_ipaddr6;
//static int resend_count = 1;
//static int done = 1;
//static int print_filename = 0;
//static int sockfd;
//static int last_used_id = -1;
//static rbtree_t *filename_tree = NULL;
static fr_packet_list_t *pl = NULL;
//static int sleep_time = -1;
pthread_mutex_t rad_mutex=PTHREAD_MUTEX_INITIALIZER;
timer_queue rad_timer_queue;
int num_sock_ipv4 = 0;
int num_sock_ipv6 = 0;


rad_recv_queue  *rad_recv_head;
rad_recv_queue  *rad_recv_tail;
unsigned int rad_recv_queue_number;
pthread_mutex_t rad_recv_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t rad_recv_cond;


struct rad_reply
{
    char *reply;
    pthread_cond_t *mycond;
    USER_TYPE user_type;
    unsigned char code;
};

typedef struct fr_packet_dst2id_t
{
    fr_ipaddr_t	dst_ipaddr;
    int		dst_port;
    uint32_t	id[1];	/* really id[256] */
} fr_packet_dst2id_t;
typedef struct fr_packet_socket_t
{
    int		sockfd;

    int		num_outgoing;

    int		offset;	/* 0..31 */
    int		inaddr_any;
    fr_ipaddr_t	ipaddr;
    int		port;
} fr_packet_socket_t;
struct fr_packet_list_t
{
    fr_hash_table_t *ht;

    fr_hash_table_t *dst2id_ht;

    int		alloc_id;
    int		num_outgoing;

    uint32_t mask;
    int last_recv;
    fr_packet_socket_t sockets[32];
};

typedef struct radclient_t
{
    struct		radclient_t *prev;
    struct		radclient_t *next;
    struct      rad_reply  *rad_reply;

    const char	*filename;
    struct timer *timer;
    int		packet_number; /* in the file */
    char		password[256];
    time_t		timestamp;
    RADIUS_PACKET	*request;
    RADIUS_PACKET	*reply;
    int		type;
    int		tries;
    int		done;
	int		reserve_done;
    int     af;
} radclient_t;

static radclient_t *radclient_head = NULL;
static radclient_t *radclient_tail = NULL;


void set_wrong_ip()
{
	/*
    server_ipaddr6.af = AF_INET6;
    if (ip_hton("::1",AF_INET6, &server_ipaddr6) < 0)
    {
        fprintf(stderr, "radclient: Failed to find IPv6 address for host %s: %s\n", hostname, strerror(errno));
    }
    server_ipaddr.af = AF_INET;
    if (ip_hton("127.0.0.1",AF_INET, &server_ipaddr) < 0)
    {
        fprintf(stderr, "radclient: Failed to find IPv4 address for host %s: %s\n", hostname, strerror(errno));
    }*/
}

int copy_message(char *dst, int dstlen, char *src, int id)
{
    int len, cplen;
    char *first, *last;
    if (id == 0) id = '\"';
    first = strchr(src, id);
    if (!first) return -1;
    last = strchr(first + 1, id);
    if (!last) return -1;
    len = last - first - 1;
    cplen = (len > dstlen - 1) ? (dstlen - 1) : len;
    memcpy(dst, first + 1, cplen);
    dst[cplen] = '\0';
    return cplen;
}

static int fr_inaddr_any(fr_ipaddr_t *ipaddr)
{
    if (ipaddr->af == AF_INET)
    {
        if (ipaddr->ipaddr.ip4addr.s_addr == INADDR_ANY)
        {
            return 1;
        }
    }
    else if (ipaddr->af == AF_INET6)
    {
        if (IN6_IS_ADDR_UNSPECIFIED(&(ipaddr->ipaddr.ip6addr)))
        {
            return 1;
        }
    }
    else
    {
        Vdebug(DEBUG_MAIN, "[radius lib]Unknown address family");
        return -1;
    }
    return 0;
}

int fr_packet_list_id_alloc_by_socket(fr_packet_list_t *pl,
                                      RADIUS_PACKET *request)
{
    int i, id, start;
    int src_any = 0;
    uint32_t free_mask;
    fr_packet_dst2id_t my_pd, *pd;
    fr_packet_socket_t *ps;

    if (!pl || !pl->alloc_id || !request) return 0;

    /*
     *	Error out if no destination is specified.
     */
    if ((request->dst_ipaddr.af == AF_UNSPEC) ||
            (request->dst_port == 0))
    {
        Vdebug(DEBUG_MAIN, "[radius lib]No destination address/port specified");
        return 0;
    }

    /*
     *	Special case: unspec == "don't care"
     */
    if (request->src_ipaddr.af == AF_UNSPEC)
    {
        memset(&request->src_ipaddr, 0, sizeof(request->src_ipaddr));
        request->src_ipaddr.af = request->dst_ipaddr.af;
    }

    src_any = fr_inaddr_any(&request->src_ipaddr);
    if (src_any < 0) return 0;

    /*
     *	MUST specify a destination address.
     */
    if (fr_inaddr_any(&request->dst_ipaddr) != 0) return 0;

    my_pd.dst_ipaddr = request->dst_ipaddr;
    my_pd.dst_port = request->dst_port;

    pd = fr_hash_table_finddata(pl->dst2id_ht, &my_pd);
    if (!pd)
    {
        pd = malloc(sizeof(*pd) + 255 * sizeof(pd->id[0]));
        if (!pd) return 0;

        memset(pd, 0, sizeof(*pd) + 255 * sizeof(pd->id[0]));

        pd->dst_ipaddr = request->dst_ipaddr;
        pd->dst_port = request->dst_port;

        if (!fr_hash_table_insert(pl->dst2id_ht, pd))
        {
            free(pd);
            return 0;
        }
    }

    /*
     *	FIXME: Go to an LRU system.  This prevents ID re-use
     *	for as long as possible.  The main problem with that
     *	approach is that it requires us to populate the
     *	LRU/FIFO when we add a new socket, or a new destination,
     *	which can be expensive.
     *
     *	The LRU can be avoided if the caller takes care to free
     *	Id's only when all responses have been received, OR after
     *	a timeout.
     */
    id = start = (int) fr_rand() & 0xff;

    while (pd->id[id] == pl->mask)   /* all sockets are using this ID */
    {
        //redo:
        id++;
        id &= 0xff;
        if (id == start) return 0;
    }

    free_mask = ~((~pd->id[id]) & pl->mask);

    start = -1;
    for (i = 0; i < 32; i++)
    {
        if (pl->sockets[i].sockfd == -1) continue; /* paranoia */

        ps = &(pl->sockets[i]);

        /*
         *	We're sourcing from *, and they asked for a
         *	specific source address: ignore it.
         */
        if (ps->inaddr_any && !src_any) continue;

        /*
         *	We're sourcing from a specific IP, and they
         *	asked for a source IP that isn't us: ignore
         *	it.
         */
        if ((fr_ipaddr_cmp(&request->src_ipaddr,
                           &ps->ipaddr) != 0)) continue;

        if ((free_mask & (1 << i)) == 0)
        {
            start = i;
            break;
        }
    }

    if (start < 0) return 0; /* bad error */

    pd->id[id] |= (1 << start);
    ps = &pl->sockets[start];

    ps->num_outgoing++;
    pl->num_outgoing++;

    /*
     *	Set the ID, source IP, and source port.
     */
    request->id = id;
    request->sockfd = ps->sockfd;
    request->src_ipaddr = ps->ipaddr;
    request->src_port = ps->port;

    return 1;
}


static int send_one_packet(radclient_t *radclient, fr_ipaddr_t ipaddr)
{
    if (radclient->tries == -1)
    {
        radclient->timer = add_timer(&rad_timer_queue, radclient);
        /*
        *	Add it to the tail of the list.
        */
        if (!radclient_head)
        {
            assert(radclient_tail == NULL);
            radclient_head = radclient;
            radclient->prev = NULL;
        }
        else
        {
            assert(radclient_tail->next == NULL);
            radclient_tail->next = radclient;
            radclient->prev = radclient_tail;
        }
        radclient_tail = radclient;
        radclient->next = NULL;
        radclient->tries = 0;
    }

    radclient->request->src_ipaddr.af = ipaddr.af;    //will be used to select local socket(ipv4 or v6)
    radclient->request->dst_ipaddr = ipaddr;//(af == AF_INET ? server_ipaddr : server_ipaddr6);

    radclient->tries++;

    //assert(radclient->done == 0);
    /*
     *	Haven't sent the packet yet.  Initialize it.
     */
    if (radclient->request->id == -1)
    {
        // printf("Initialize packet...\n");
        int i, rcode;

        assert(radclient->reply == NULL);

        /*
         *	Didn't find a free packet ID, we're not done,
         *	we don't sleep, and we stop trying to process
         *	this packet.
         */
retry:
        //radclient->request->src_ipaddr.af = AF_UNSPEC;
        rcode = fr_packet_list_id_alloc_by_socket(pl, radclient->request);

        if (rcode == 0)
        {
            if (radclient->request->id == -1)
            {
                if (radclient->request->src_ipaddr.af == AF_INET)
                {
                    if (num_sock_ipv4 == 10)
                    {
                        return 0;
                    }
                    num_sock_ipv4++;
                }
                else
                {
                    if (num_sock_ipv6 == 10)
                    {
                        return 0;
                    }
                    num_sock_ipv6++;
                }
                int mysockfd;
                mysockfd = fr_socket(&radclient->request->src_ipaddr, 0);
                if (!mysockfd)
                {
                    Vdebug(DEBUG_MAIN, "[radius send]Can't open new socket");
                    return 0;
                }
                if (!fr_packet_list_socket_add(pl, mysockfd))
                {
                    Vdebug(DEBUG_MAIN, "[radius send]Can't add new socket");
                    return 0;
                }
                Vdebug(DEBUG_BOTH, "[radius send]Warning!!\nNo free id alloc to raiud packet. Open a new socket %d", mysockfd);
                goto retry;
            }
            else
            {
                Vdebug(DEBUG_MAIN, "[radius send]error happen alloc rad packet id!! Check your code~");
            }
            return 0;
        }

        assert(radclient->request->id != -1);
        assert(radclient->request->data == NULL);

        for (i = 0; i < 4; i++)
        {
            ((uint32_t *) radclient->request->vector)[i] = fr_rand();
        }

        /*
         *	Update the password, so it can be encrypted with the
         *	new authentication vector.
         */
        if (radclient->password[0] != '\0')
        {
            VALUE_PAIR *vp;

            if ((vp = pairfind(radclient->request->vps, PW_USER_PASSWORD)) != NULL)
            {
                strlcpy(vp->vp_strvalue, radclient->password,
                        sizeof(vp->vp_strvalue));
                vp->length = strlen(vp->vp_strvalue);

            }
            else if ((vp = pairfind(radclient->request->vps, PW_CHAP_PASSWORD)) != NULL)
            {
                /*
                 *	FIXME: AND there's no CHAP-Challenge,
                 *	       AND vp->length != 17
                 *	       AND rad_chap_encode() != vp->vp_octets
                 */
                strlcpy(vp->vp_strvalue, radclient->password,
                        sizeof(vp->vp_strvalue));
                vp->length = strlen(vp->vp_strvalue);

                rad_chap_encode(radclient->request,
                                vp->vp_octets,
                                radclient->request->id, vp);
                vp->length = 17;
            }
        }
        /*
         *	Duplicate found.  Serious error!
         */
        /*
        getnameinfo((struct sockaddr *)&(radclient->request->src_ipaddr), \
                     (radclient->request->src_ipaddr).af == PF_INET ? sizeof(struct sockaddr_in): sizeof(struct sockaddr_in6), \
                     ipaddr, 40, NULL, 0, NI_NUMERICHOST);
        printf("Init packet where source port %d, ip %s", (radclient->request->src_port), ipaddr);*/

        if (!fr_packet_list_insert(pl, &radclient->request))
        {
            assert(0 == 1);
        }
    }
    /*
    *	Send the packet.
    */
//COUNT_START(a)
    if (rad_send(radclient->request, NULL, secret) < 0)
    {
        Vdebug(DEBUG_BOTH, "[radius send]Failed to send packet for ID %d: %s",
                radclient->request->id, fr_strerror());
    }
//COUNT_STOP(a)
//PRINT_TIME(a)
    return 0;
}


radclient_t * rad_packet_init(const char *name, const char *pass)
{
    radclient_t *radclient;
    int packet_number = 1;
    VALUE_PAIR *vp;

    radclient = malloc(sizeof(*radclient));
    if (!radclient)
    {
        perror("radclient: error malloc radclient.");
        return 0;
    }
    memset(radclient, 0, sizeof(*radclient));
    radclient->request = rad_alloc(1);
    if (!radclient->request)
    {
        fr_perror("radclient: Y");
        free(radclient);
        return 0;
    }
    radclient->request->id = -1; /* allocate when sending */
    radclient->packet_number = packet_number++;
    radclient->af = radius_default_afnum;
    radclient->done = !(radius_server_ipv6 && radius_server_ipv4);
    pairadd(&radclient->request->vps, pairmake("User-Name", name, 11));
    pairadd(&radclient->request->vps, pairmake("User-Password", pass, 11));
    // pairadd(&radclient->request->vps, pairmake("Veno-User-Type", "Veno-User-VIP", 11));
    //pairadd(&radclient->request->vps, pairmake("Veno-User-Name", "aidi", 11));
    //pairadd(&radclient->request->vps, pairmake("Veno-User-Ipaddr", "2001:c68:3402:7::3", 11));
    //pairadd(&radclient->request->vps, pairmake("Veno-User-Access", pass, 11));
//   pairadd(&radclient->request->vps, pairmake("Acct-Status-Type", "Start", 11));
    // vp_printlist(stdout, radclient->request->vps);
    /*
     *	Keep a copy of the the User-Password attribute.
     */
    if ((vp = pairfind(radclient->request->vps, PW_USER_PASSWORD)) != NULL)
    {
        strlcpy(radclient->password, vp->vp_strvalue,
                sizeof(radclient->password));
        /*
        *	Otherwise keep a copy of the CHAP-Password attribute.
        	 */
    }
    else if ((vp = pairfind(radclient->request->vps, PW_CHAP_PASSWORD)) != NULL)
    {
        strlcpy(radclient->password, vp->vp_strvalue,
                sizeof(radclient->password));
    }
    else
    {
        radclient->password[0] = '\0';
    }
    /*
    *  Fix up Digest-Attributes issues
    */
    for (vp = radclient->request->vps; vp != NULL; vp = vp->next)
    {
        switch (vp->attribute)
        {
        default:
            break;

            /*
             *	Allow it to set the packet type in
             *	the attributes read from the file.
             */
        case PW_PACKET_TYPE:
            radclient->request->code = vp->vp_integer;
            break;

        case PW_PACKET_DST_PORT:
            radclient->request->dst_port = (vp->vp_integer & 0xffff);
            break;

        case PW_PACKET_DST_IP_ADDRESS:
            radclient->request->dst_ipaddr.af = AF_INET;
            radclient->request->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
            break;

        case PW_PACKET_DST_IPV6_ADDRESS:
            radclient->request->dst_ipaddr.af = AF_INET6;
            radclient->request->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
            break;

        case PW_PACKET_SRC_PORT:
            radclient->request->src_port = (vp->vp_integer & 0xffff);
            break;

        case PW_PACKET_SRC_IP_ADDRESS:
            radclient->request->src_ipaddr.af = AF_INET;
            radclient->request->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
            break;

        case PW_PACKET_SRC_IPV6_ADDRESS:
            radclient->request->src_ipaddr.af = AF_INET6;
            radclient->request->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
            break;

        case PW_DIGEST_REALM:
        case PW_DIGEST_NONCE:
        case PW_DIGEST_METHOD:
        case PW_DIGEST_URI:
        case PW_DIGEST_QOP:
        case PW_DIGEST_ALGORITHM:
        case PW_DIGEST_BODY_DIGEST:
        case PW_DIGEST_CNONCE:
        case PW_DIGEST_NONCE_COUNT:
        case PW_DIGEST_USER_NAME:
            /* overlapping! */
            memmove(&vp->vp_octets[2], &vp->vp_octets[0],
                    vp->length);
            vp->vp_octets[0] = vp->attribute - PW_DIGEST_REALM + 1;
            vp->length += 2;
            vp->vp_octets[1] = vp->length;
            vp->attribute = PW_DIGEST_ATTRIBUTES;
            break;
        }
    }
    radclient->request->src_port = 0;
    radclient->request->dst_port = PW_AUTH_UDP_PORT;
    radclient->request->code = PW_AUTHENTICATION_REQUEST;
    radclient->request->sockfd = -1;
    radclient->tries = -1;
    return radclient;
}

int rad_account_packet_send(struct table_chain *table, ACCT_TYPE acct_type, const char *reason, void *rad_reply)
{
    radclient_t *radclient;
    int packet_number = 1;
    VALUE_PAIR *vp;
    const char *name, *ip, *id, *ver;
    char *status = 0;
    char kick_time[100] = "3";
	char temp[100] ={0};
	unsigned int t;

    if (do_radius_account == 0)
    {
        return 0;
    }

    name = table->table->name;
    ip = table->table->ipaddr;
    id = table->table->session_tx;
    ver = table->table->version;
    
    radclient = malloc(sizeof(*radclient));
    if (!radclient)
    {
        perror("radclient: error malloc radclient.");
        return 0;
    }
    memset(radclient, 0, sizeof(*radclient));
    switch (acct_type)
    {
    case ACCT_START:
        radclient->type = 1;
        status = "Start";
        break;
    case ACCT_STOP:
        radclient->type = 0;
        status = "Stop";
        break;
    case ACCT_ALIVE:
        radclient->type = 3;
        status = "Alive";
        break;
    }
    radclient->request = rad_alloc(1);
    if (!radclient->request)
    {
        fr_perror("radclient: Y");
        free(radclient);
        return 0;
    }
    radclient->done = !(radius_server_ipv6 && radius_server_ipv4);
    radclient->rad_reply = (struct rad_reply *)rad_reply;
    radclient->request->id = -1; /* allocate when sending */
    radclient->packet_number = packet_number++;
    radclient->af = radius_default_afnum;
    pairadd(&radclient->request->vps, pairmake("User-Name", name, 11));
    pairadd(&radclient->request->vps, pairmake("NAS-IP-Address", nas_ip4addr, 11));
    pairadd(&radclient->request->vps, pairmake("Calling-Station-Id", ip, 11));
    pairadd(&radclient->request->vps, pairmake("Acct-Status-Type", status, 11));
    pairadd(&radclient->request->vps, pairmake("Acct-Session-Id", id, 11));
	if (!strstr(status, "Start"))
	{
		t = table->table->total_down_traffic >> 22;  //giga words
		sprintf(temp, "%u", t);
		pairadd(&radclient->request->vps, pairmake("Acct-Input-Gigawords", temp, 11));
		memset(temp, 0 , sizeof(temp));
		t = (table->table->total_down_traffic - (t << 22)) << 10; 
		sprintf(temp, "%u", t);
		pairadd(&radclient->request->vps, pairmake("Acct-Input-Octets", temp, 11));
		memset(temp, 0 , sizeof(temp));


		t = table->table->total_up_traffic >> 22;  //giga words
		sprintf(temp, "%u", t);
		pairadd(&radclient->request->vps, pairmake("Acct-Output-Gigawords", temp, 11));
		memset(temp, 0 , sizeof(temp));
		t = (table->table->total_up_traffic - (t << 22)) << 10; 
		sprintf(temp, "%u", t);
		pairadd(&radclient->request->vps, pairmake("Acct-Output-Octets", temp, 11));
		memset(temp, 0 , sizeof(temp));	
	}
	else
	{
		pairadd(&radclient->request->vps, pairmake("Connect-Info", table->table->version, 11));
	}
    if (reason)
    {
        if ((strcmp(reason, "Admin-Reset") == 0) ||
			(strcmp(reason, "Host-Request") == 0))
        {
            if (table->table->kick_time)
            {
                memset(kick_time, 0 ,sizeof(kick_time));
                sprintf(kick_time, "%d", table->table->kick_time);
            }
            pairadd(&radclient->request->vps, pairmake("Session-Timeout", kick_time, 11));
        }
        pairadd(&radclient->request->vps, pairmake("Acct-Terminate-Cause", reason, 11));
    }
//   pairadd(&radclient->request->vps, pairmake("Acct-Status-Type", "Start", 11));
    /*
     *	Keep a copy of the the User-Password attribute.
     */
    if ((vp = pairfind(radclient->request->vps, PW_USER_PASSWORD)) != NULL)
    {
        strlcpy(radclient->password, vp->vp_strvalue,
                sizeof(radclient->password));
        /*
        *	Otherwise keep a copy of the CHAP-Password attribute.
        	 */
    }
    else if ((vp = pairfind(radclient->request->vps, PW_CHAP_PASSWORD)) != NULL)
    {
        strlcpy(radclient->password, vp->vp_strvalue,
                sizeof(radclient->password));
    }
    else
    {
        radclient->password[0] = '\0';
    }

    radclient->request->src_port = 0;
    radclient->request->dst_port = PW_ACCT_UDP_PORT;
    radclient->request->code = PW_ACCOUNTING_REQUEST;
    radclient->request->sockfd = -1;

    //radclient->mycond = NULL;
    radclient->tries = -1;
	pthread_mutex_lock(&rad_mutex);
    send_one_packet(radclient, radclient->af == AF_INET ? server_ipaddr : server_ipaddr6);
    pthread_mutex_unlock(&rad_mutex);
    return 1;
}

int rad_send_test_packet(char *ip, int size, fr_ipaddr_t dest_ip)
{
    pthread_cond_t mycond;
    char reply[100] = {0};
    struct rad_reply rad_reply;
    radclient_t *radclient;

    *(short *)reply = sizeof(reply);

    pthread_cond_init(&mycond,NULL);

    memset(&rad_reply, 0 ,sizeof(struct rad_reply));
    rad_reply.reply = reply;
    
    radclient = malloc(sizeof(*radclient));
    if (!radclient)
    {
        perror("radclient: error malloc radclient.");
        return 0;
    }
    memset(radclient, 0, sizeof(*radclient));
    radclient->request = rad_alloc(1);
    if (!radclient->request)
    {
        fr_perror("radclient: Y");
        free(radclient);
        return 0;
    }
    rad_reply.mycond = &mycond;
    radclient->rad_reply = &rad_reply;
    radclient->request->id = -1; /* allocate when sending */
    radclient->packet_number = 0;
    radclient->af = dest_ip.af;
    radclient->type = 1;
    radclient->done = 1;
	radclient->reserve_done = 1;
    pairadd(&radclient->request->vps, pairmake("Veno-Packet-Type", "Veno-Packet-Test", 11));
//   pairadd(&radclient->request->vps, pairmake("Acct-Status-Type", "Start", 11));
    /*
     *	Keep a copy of the the User-Password attribute.
     */
    radclient->request->src_port = 0;
    radclient->request->dst_port = PW_ACCT_UDP_PORT;
    radclient->request->code = PW_ACCOUNTING_REQUEST;
    radclient->request->sockfd = -1;
    radclient->tries = -1;

	pthread_mutex_lock(&rad_mutex);
    send_one_packet(radclient, dest_ip);
    pthread_cond_wait(&mycond, &rad_mutex);
    pthread_mutex_unlock(&rad_mutex);
    pthread_cond_destroy(&mycond);
    if (rad_reply.code)
    {
        copy_message(ip, size, reply, '\"');
        return 1;
    }
    else
    {
        Vdebug(DEBUG_DATE, "[radius client]Warning : Could not connect to radius server using %s method", dest_ip.af == AF_INET ? "IPV4" : "IPV6");
        return 0;
    }
}
/*
static int getport(const char *name)
{
    struct	servent		*svp;

    svp = getservbyname (name, "udp");
    if (!svp)
    {
        return 0;
    }

    return ntohs(svp->s_port);
}*/

static void deallocate_id(radclient_t *radclient)
{
    if (!radclient || !radclient->request ||
            (radclient->request->id < 0))
    {
        return;
    }

    /*
     *	One more unused RADIUS ID.
     */
    fr_packet_list_id_free(pl, radclient->request);
    radclient->request->id = -1;

    /*
     *	If we've already sent a packet, free up the old one,
     *	and ensure that the next packet has a unique
     *	authentication vector.
     */
    if (radclient->request->data)
    {
        free(radclient->request->data);
        radclient->request->data = NULL;
    }

    if (radclient->reply) rad_free(&radclient->reply);
}

/*
 *	Free a radclient struct, which may (or may not)
 *	already be in the list.
 */
void radclient_free(radclient_t *radclient)
{
    radclient_t *prev, *next;

    if (radclient->request) rad_free(&radclient->request);
    if (radclient->reply) rad_free(&radclient->reply);

    prev = radclient->prev;
    next = radclient->next;

    if (prev)
    {
        assert(radclient_head != radclient);
        prev->next = next;
    }
    else if (radclient_head)
    {
        assert(radclient_head == radclient);
        radclient_head = next;
    }

    if (next)
    {
        assert(radclient_tail != radclient);
        next->prev = prev;
    }
    else if (radclient_tail)
    {
        assert(radclient_tail == radclient);
        radclient_tail = prev;
    }

    free(radclient);
}

VALUE_PAIR *pair_find_by_name(VALUE_PAIR *vplist, const char *c)
{
    VALUE_PAIR *vp;
    for (vp = vplist; vp; vp = vp->next)
    {
        if (strcmp(c, vp->name) == 0)
            return vp;
    }
    return NULL;
}

int vps_prints(char *out, size_t outlen, VALUE_PAIR *vp)
{
    VALUE_PAIR *v;
    size_t		len;
    //const char	*token = NULL;
    const char	*name;
    char		namebuf[128];

    out[0] = 0;
    len = 0;

    for (v = vp; v; v = v->next)
    {
        if (v != vp)
        {
            if (len < outlen - 1)
            {
                out[len] = '&';
                out[len + 1] = 0;
                len++;
            }
            else return len;
        }
        name = v->name;

        if (!name || !*name)
        {
            if (!vp_print_name(namebuf, sizeof(namebuf), v->attribute))
            {
                return 0;
            }
            name = namebuf;
        }
        snprintf(out + len, outlen - len, "%s%s", name, "=");
        len = strlen(out);
        vp_prints_value(out + len, outlen - len, v, 1);
        len = strlen(out);
    }
    return len;
}

void rad_packet_process()
{
    VALUE_PAIR *vp;
    radclient_t	*radclient;
    RADIUS_PACKET	*reply, **request_p;
    short replylen;
    //unsigned int FromLen;

    struct rad_recv_queue *queue = NULL;

    //FromLen = sizeof(struct sockaddr_storage);
    while (1)
    {
        if (queue)
        {
            rad_free(&reply);
            free(queue);
        }
        pthread_mutex_lock(&rad_recv_mutex);
        while (rad_recv_head == NULL)
        {
            pthread_cond_wait(&rad_recv_cond, &rad_recv_mutex);
            continue;
        }
        //	queue_number--;
        queue = rad_recv_head;
        rad_recv_head = rad_recv_head->next;
        if (!rad_recv_head)
        {
            rad_recv_tail = NULL;
        }
        pthread_mutex_unlock(&rad_recv_mutex);
        reply = (RADIUS_PACKET *)queue->buf;

        pthread_mutex_lock(&rad_mutex);
        /*
         *	udpfromto issues.  We may have bound to "*",
         *	and we want to find the replies that are sent to
         *	(say) 127.0.0.1.
         */
        reply->dst_ipaddr.af = reply->src_ipaddr.af ;

        request_p = fr_packet_list_find_byreply(pl, reply);
        if (!request_p)
        {
            Vdebug(DEBUG_DATE, "[radius receive]received response to request we did not send. (id=%d socket %d)", reply->id, reply->sockfd);
            pthread_mutex_unlock(&rad_mutex);
            continue;
        }
        radclient = fr_packet2myptr(radclient_t, request, request_p);

        /*
         *	Fails the signature validation: not a real reply.
         *	FIXME: Silently drop it and listen for another packet.
         */
        if (rad_verify(reply, radclient->request, secret) < 0)
        {
            fr_perror("rad_verify");
            //totallost++;
            pthread_mutex_unlock(&rad_mutex);
            continue;
        }

        fr_packet_list_yank(pl, radclient->request);
        deallocate_id(radclient);
        radclient->reply = reply;
        reply = NULL;
        /*
         *	If this fails, we're out of memory.
         */
        if (rad_decode(radclient->reply, radclient->request, secret) != 0)
        {
            fr_perror("rad_decode\n");
            //totallost++;
            pthread_mutex_unlock(&rad_mutex);
            continue;
        }
        del_timer(radclient->timer);
        if (radclient->rad_reply && radclient->rad_reply->mycond)
        {
            radclient->rad_reply->code = radclient->reply->code;
            // libradius debug already prints out the value pairs for us
            /*
            printf("Radius client received response ID %d, code %d, length = %d\n",
                   radclient->reply->id, radclient->reply->code,
                   radclient->reply->data_len);*/
            /*  if ((vp = pairfind(radclient->reply->vps, PW_REPLY_MESSAGE)) != NULL)
              {
                  //printf("name %s, attribute %d, vendor %d, type %d, lvalue %d\n",vp->name, vp->attribute, vp->vendor, vp->type, vp->lvalue);
                  if (radclient->rad_reply->reply)
                      vp_prints(radclient->rad_reply->reply, 100, vp);
              }*/
            if (radclient->rad_reply->reply)
            {
                replylen = *(short *)(radclient->rad_reply->reply);
                memset(radclient->rad_reply->reply, 0 ,replylen);
                vps_prints(radclient->rad_reply->reply, replylen, radclient->reply->vps);
            }
            if ((vp = pair_find_by_name(radclient->reply->vps, "Veno-User-Type")) != NULL)
            {
                // printf("name %s, attribute %d, vendor %d, type %d, lvalue %d\n",vp->name, vp->attribute, vp->vendor, vp->type, vp->lvalue);
                radclient->rad_reply->user_type = vp->lvalue;
            }
            //vp_printlist(stdout, radclient->reply->vps);
            pthread_cond_signal(radclient->rad_reply->mycond);
        }
        //printf("Receive radius response\n");
        radclient_free(radclient);
        pthread_mutex_unlock(&rad_mutex);
    }
}


static int recv_one_packet()
{

    fd_set		set;
    RADIUS_PACKET	*reply;
    volatile int max_fd;
    int result;
    pthread_t rad_recv_thread;
    rad_recv_queue *queue;

    pthread_cond_init(&rad_recv_cond,NULL);
    rad_recv_queue_number = 0;
    rad_recv_head = NULL;
    rad_recv_tail = NULL;
    if (pthread_create( &rad_recv_thread, NULL, (void *)rad_packet_process, NULL) )
    {
        Vdebug(DEBUG_MAIN, "[radius client]error creating udp packet process thread.");
        abort();
    }
    //  printf("Radius client thread start..\n");
    while (1)
    {
        /* And wait for reply, timing out as necessary */
        FD_ZERO(&set);
        max_fd = fr_packet_list_fd_set(pl, &set);
        if (max_fd < 0) exit(1); /* no sockets to listen on! */

        result = select(max_fd, &set, NULL, NULL, NULL);
        if (result <=0) return 0;

        /*
         *	Look for the packet.
         */
        reply = fr_packet_list_recv(pl, &set);
        if (!reply)
        {
            Vdebug(DEBUG_BOTH, "[radius receive]received bad packet: %s",
                    fr_strerror());
            continue;
        }
        queue = malloc(sizeof(rad_recv_queue));
        if (!queue)
        {
            rad_free(&reply);
            continue;
        }
        memset(queue , 0, sizeof(rad_recv_queue));
        queue->buf = reply;
        queue->next = NULL;
        pthread_mutex_lock(&rad_recv_mutex);
        rad_recv_queue_number++;
        queue->num = rad_recv_queue_number;
        if (!rad_recv_head)
        {
            rad_recv_head = queue;
            rad_recv_tail = queue;
            pthread_mutex_unlock(&rad_recv_mutex);
            pthread_cond_signal(&rad_recv_cond);
        }
        else
        {
            //assert(tail != NULL);
            rad_recv_tail->next = queue;
            rad_recv_tail = queue;
            pthread_mutex_unlock(&rad_recv_mutex);
        }

    }
    return 0;
}

int
rad_send_timeout_callback(radclient_t *radclient )
{
    if (!radclient) return 1;
    switch (radclient->type)          //important radius packet
    {
    case 0:
        retries = RAD_RESEND_TIMES * 2;
        break;
    case 1:
        retries = RAD_RESEND_TIMES;
        break;
    case 3:
        retries = 1;
        break;
    }
    if (radclient->tries >= retries)
    {
        if (radclient->request->id >= 0)
        {
            /* radclient->done indicate whether should try another inet type to connect to radius server */
            if (radclient->done == 0) //|| (radclient->type == 0))   // never discard type 0 packet
            {
                if (radclient->done == 0)
                {
                    radclient->af = (radclient->af == AF_INET) ? AF_INET6 : AF_INET;
                }
                Vdebug(DEBUG_DATE, "[radius client]server no response(ID %d socket %d tries %d).Try %s",
                       radclient->request->id, radclient->request->sockfd, retries, radclient->af == AF_INET ? "ipv4" : "ipv6");
                fr_packet_list_yank(pl, radclient->request);
                deallocate_id(radclient);
                radclient->tries = 0;
                radclient->done = 1;
                send_one_packet(radclient, radclient->af == AF_INET ? server_ipaddr : server_ipaddr6);
                return 0;
            }
            else
            {
                //	Delete the request from the tree of outstanding requests.
                fr_packet_list_yank(pl, radclient->request);
                deallocate_id(radclient);
				if (radclient->reserve_done == 0)
				{
					if (radius_reserve_server_ipv4) {
						Vdebug(DEBUG_BOTH, "[radius client] Use reserve server(ipv4)");
						radclient->af = AF_INET;
						radclient->tries = 0;
						radclient->reserve_done = 1;
						send_one_packet(radclient,  reserve_server_ipaddr); 
						return 0;
					}
					else if (radius_reserve_server_ipv6) {
						Vdebug(DEBUG_BOTH, "[radius client] Use reserve server(ipv6)");
						radclient->af = AF_INET6;
						radclient->tries = 0;
						radclient->reserve_done = 1;
						send_one_packet(radclient,  reserve_server_ipaddr6); 
						return 0;
					}
					// delete packet
					Vdebug(DEBUG_BOTH, "[radius client]server no response(ID %d socket %d tries %d).Delete the request",
                            radclient->request->id, radclient->request->sockfd, retries);
				}
				
            }
        }
        else if (radclient->type == 0)  /* Accounting stop packet. Never discard. */
        {
            radclient->tries = 0;
            send_one_packet(radclient, radclient->af == AF_INET ? server_ipaddr : server_ipaddr6);
            return 0;
        }
        //discard the packet
        if (radclient->rad_reply && radclient->rad_reply->mycond)
        {
            pthread_cond_signal(radclient->rad_reply->mycond);  // announce the waiting thread
        }
        radclient_free(radclient);
        return 1;   //delete the timer
    }
    else
    {
        Vdebug(DEBUG_DATE, "[radius client]server(%s) no response(ID %d socket %d). Resend for the %d time.",
               radclient->af == AF_INET ? "ipv4" : "ipv6",radclient->request->id, radclient->request->sockfd, radclient->tries);
        if (radclient->reserve_done == 1)
			send_one_packet(radclient, radclient->af == AF_INET ? reserve_server_ipaddr : reserve_server_ipaddr6);
		else
			send_one_packet(radclient, radclient->af == AF_INET ? server_ipaddr : server_ipaddr6);
        return 0;   //update the timer
    }
}


int rad_init()
{
    const char *radius_dir = RADDBDIR;
    int force_af = AF_UNSPEC;
    int sockfd, sockfd6;
    static fr_ipaddr_t client_ipaddr;
    int client_port;
	int ret;

    secret = radius_shared_key;

    timer_init(&rad_timer_queue, (void *)rad_send_timeout_callback, RAD_RESEND_TIME_INTERVAL, &rad_mutex);

    if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0)
    {
        Vdebug(DEBUG_MAIN, "[radius client init]error read dictionary");
        return 0;
    }

    if (radius_server_ipv6)
    {
        hostname = radius_server_ipv6;
        server_ipaddr6.af = AF_INET6;
        //if (ip_hton(hostname, force_af, &server_ipaddr6) < 0)
		if ((ret = inet_pton(AF_INET6, hostname, &server_ipaddr6.ipaddr)) <= 0)
        {
            Vdebug(DEBUG_MAIN, "[radius client init]Failed to find IPv6 address for host %s: %d", hostname, ret);
            return 0;
        }
    }
    if (radius_server_ipv4)
    {
        hostname = radius_server_ipv4;
        server_ipaddr.af = AF_INET;
        if (ip_hton(hostname, force_af, &server_ipaddr) < 0)
        {
            Vdebug(DEBUG_MAIN, "[radius client init]Failed to find IPv4 address for host %s: %s", hostname, strerror(errno));
            return 0;
        }
    }
    if (radius_reserve_server_ipv6)
    {
        hostname = radius_reserve_server_ipv6;
        reserve_server_ipaddr6.af = AF_INET6;
        //if (ip_hton(hostname, force_af, &server_ipaddr6) < 0)
		if ((ret = inet_pton(AF_INET6, hostname, &reserve_server_ipaddr6.ipaddr)) <= 0)
        {
            Vdebug(DEBUG_MAIN, "[radius client init]Failed to find reserve_IPv6 address for host %s: %d", hostname, ret);
            return 0;
        }
    }
    if (radius_reserve_server_ipv4)
    {
        hostname = radius_reserve_server_ipv4;
        reserve_server_ipaddr.af = AF_INET;
        if (ip_hton(hostname, force_af, &reserve_server_ipaddr) < 0)
        {
            Vdebug(DEBUG_MAIN, "[radius client init]Failed to find IPv4 address for host %s: %s", hostname, strerror(errno));
            return 0;
        }
    }
    memset(&client_ipaddr, 0, sizeof(client_ipaddr));
    client_ipaddr.af = AF_INET;
    client_port = 0;
    sockfd = fr_socket(&client_ipaddr, client_port);
    if (sockfd < 0)
    {
        Vdebug(DEBUG_MAIN, "[radius client init]IPV4 socket: %s", fr_strerror());
        return 0;
    }
    client_ipaddr.af = AF_INET6;
    sockfd6 = fr_socket(&client_ipaddr, client_port);
    if (sockfd6 < 0)
    {
        Vdebug(DEBUG_MAIN, "[radius client init]IPV6 socket: %s", fr_strerror());
        return 0;
    }
    pl = fr_packet_list_create(1);
    if (!pl)
    {
        Vdebug(DEBUG_MAIN, "[radius client init]Out of memory");
        return 0;
    }

    if (!fr_packet_list_socket_add(pl, sockfd))
    {
        Vdebug(DEBUG_MAIN, "[radius client init]Out of memory");
        return 0;
    }
    if (!fr_packet_list_socket_add(pl, sockfd6))
    {
        Vdebug(DEBUG_MAIN, "[radius client init]Out of memory");
        return 0;
    }
    /* Create packet receiving thread */
    pthread_t mythread;

    if ( pthread_create( &mythread, NULL, (void *)recv_one_packet, NULL) )
    {
        Vdebug(DEBUG_MAIN, "[radius client init]error creating packet receiving thread.");
        return 0;
    }
    return 1;
}

int rad_test()
{
    if (do_radius_test)	sleep(1);  //wait the receive thread start
    if (do_radius_test)
    {
        Vdebug(DEBUG_MAIN, "[radius test]Testing IPV4 connection to radius server...");
    }
    if (do_radius_test && rad_send_test_packet(nas_ip4addr, 19, server_ipaddr))
    {
        Vdebug(DEBUG_MAIN, "[radius test]Set NAS IPV4 address: %s", nas_ip4addr);
    }
    else
    {
        if (!local_server_ipv4)
        {
            Vdebug(DEBUG_MAIN, "[radius test]Warning...No local server ipv4 address set.");
        }
        else
        {
            memcpy(nas_ip4addr, local_server_ipv4, MIN(sizeof(nas_ip4addr), strlen(local_server_ipv4)));
            Vdebug(DEBUG_MAIN, "[radius test]Set default NAS IPV4 address: %s", nas_ip4addr);
        }
    }
    if (do_radius_test)
    {
        Vdebug(DEBUG_MAIN, "[radius test]Testing IPV6 connection to radius server...");
    }
    if (do_radius_test && rad_send_test_packet(nas_ip6addr, 39, server_ipaddr6))
    {
        Vdebug(DEBUG_MAIN, "[radius test]Set NAS IPV6 address: %s", nas_ip6addr);
    }
    else
    {
        if (!local_server_ipv6)
        {
            Vdebug(DEBUG_MAIN, "[radius test]Warning...No local server ipv6 address set.");
        }
        else
        {
            memcpy(nas_ip6addr, local_server_ipv6, MIN(sizeof(nas_ip6addr), strlen(local_server_ipv6)));
            Vdebug(DEBUG_MAIN, "[radius test]Set default NAS IPV6 address: %s", nas_ip6addr);
        }
    }
    return 1;
}

int rad_send_request(const char *name, const char *pass, struct table_chain *chain, char *reply, USER_TYPE *user_type)
{
    pthread_cond_t mycond;
    int result;
    pthread_cond_init(&mycond,NULL);
    radclient_t *radclient = 0;
    struct rad_reply rad_reply;

    memset(&rad_reply, 0 ,sizeof(struct rad_reply));
    radclient = rad_packet_init(name, pass);
    if (!radclient)
    {
        Vdebug(DEBUG_MAIN, "[radius auth]error initialize packet");
        exit(1);
    }
    switch (chain->table->login_type)
    {
    case CLIENT_AUTH_REQUEST:
    case CLIENT_REAUTH_REQUEST:
        pairadd(&radclient->request->vps, pairmake("Veno-Packet-Type", "Veno-Packet-ForceOff", 11));
        break;
    }
    rad_reply.reply = reply;
    rad_reply.mycond = &mycond;
    radclient->rad_reply = &rad_reply;
    radclient->type = 1;

	pthread_mutex_lock(&rad_mutex);
    send_one_packet(radclient, radclient->af == AF_INET ? server_ipaddr : server_ipaddr6);
    
    //  printf("Thread (pid = %u) sleep waiting\n", (unsigned int)pthread_self());
    pthread_cond_wait(&mycond, &rad_mutex);
    pthread_mutex_unlock(&rad_mutex);
    // printf("wake up\n");

    *user_type = rad_reply.user_type;
    result = rad_reply.code ? rad_reply.code : -1;    //-1 auth time out
    if (do_radius_account && (rad_reply.code == 2))
    {
        rad_reply.reply = NULL;
        rad_account_packet_send(chain, ACCT_START, NULL, &rad_reply);
        //printf("Thread (pid = %u) sleep waiting\n", (unsigned int)pthread_self());
        pthread_cond_wait(&mycond, &rad_mutex);
        pthread_mutex_unlock(&rad_mutex);
        //printf("wake up\n");
        result = rad_reply.code == 5 ? result : -2;
    }

    pthread_cond_destroy(&mycond);

    return result;
}

int admin_radius(char *reply)
{
    int n = 0, n1 = 0 , n2 = 0;

    pthread_mutex_lock(&rad_mutex);
    n = fr_packet_list_num_elements(pl);
    n1 = fr_packet_list_num_incoming(pl);
    n2 = fr_packet_list_num_outgoing(pl);
    pthread_mutex_unlock(&rad_mutex);
    sprintf(reply, "Number of radius packet:\n  waiting in chain: %d \n  incoming: %d \n  outgoing: %d \n", n, n1, n2);
    Vdebug(DEBUG_DATE, "[admin]%s", reply);
    return 0;
}
